
var widget_total = 0;
var widget_current = 0;
var widget_draging_id = 0;
var widget_drag;

// CUSTOM CODE
func.widget = {};

func.widget.offset = function(selector) {
    var f = $(selector).offset();
    console.log(f);
}

func.widget.noticeChange = function(style,text)
{
    var html = '<div id="" class="alert alert-'+style+' alert-dismissable">';
    html+='<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>';
    html+=text;
    html+='</div>';
    $('#profile_notice').html(html);
    $('.alert-'+style).css({
        'background-image':'none',
        'padding-top':'10px',
        'padding-bottom':'10px'
    });
    $('#profile_notice').fadeIn('slow', function(){
        setTimeout(function() {
            $('#profile_notice').fadeOut();
        },1500);
    });
}

func.widget.addNumberWigdet = function(num) {
    widget_total+=num;
}

func.widget.addCurrentWidget = function(num) {
    widget_current+=num;
}

func.widget.updateProgress = function() {
    var profile_percent = Math.round(widget_current/widget_total*100);
    $('#profile_progress').attr('aria-valuemax', profile_percent);
    $('#profile_progress').animate({'width':profile_percent+'%'}, 500);
};

func.widget.startDrag = function(element) {
    console.log('START DRAG');
    widget_draging_id = $(element).attr('num');
    $('.user_widget_block').attr({
        'data-drop':'true',
        'data-jqyoui-options':'data-jqyoui-options',
        'jqyoui-droppable':'jqyoui-droppable'
    });
    var num = $(element).attr('num');
    $('#widget_id_' + num).fadeIn(100).css({
        'border': '1px dashed #999',
        'margin': '2px',
        'margin-bottom': '9px'
    });
    //updateDroppable();
};

func.widget.inDrag = function(element) {
    var $t = $(element);
    var top = func.px($t.css('top')),
        width = func.px($t.css('width')),
        height = func.px($t.css('height')),
        left = func.px($t.css('left')),
        num = $t.attr('num'),
        max_width = $('#widget_column').css('width');

    for (widget in widgets) {
        var id = widgets[widget].id;
        var display = $('#user_block_'+id).css('display');
        if (display==='block') {
            if (func.checkIntersecion(element, '#widget_block_'+id)) {
                $('#widget_block_'+num).css({'display':'none'});
                widget_drag = $('#user_block_'+num);
                widget_drag.css({border:'1px dashed #ccc'}).fadeIn();
                break;
            }
        }
    }
};

func.widget.stopDrag = function(element) {
    var id = $(element).attr('num');
    console.log('ID ='+id);
    $('#user_block_'+id).css({
        border:'none'
    });

    send('getwidget', {widget_id: id}, function(result) {
        $('#widget_content_'+id).html(result);
    });
};

func.widget.overDrop = function(element) {
    console.log('OVER DROP');

    $('.user_profile_stack').css({
        border:'2px dashed #999'
    });

    // COMFORTABLE POSITION
    if ($(element).hasClass('user_widget_block')) {
       return;
    }
        /*
        $t = $(element);
        var id = $t.attr('num');

        $('#widget_block_'+widget_draging_id).css({
            'display':'none',
        });

        $('#user_block_'+widget_draging_id).css({
            'display':'block',
            'margin-bottom': '12px'
        });

        console.log('SWAP '+widget_draging_id+' vs '+id);
        var block_new = 'user_block_'+widget_draging_id;
        var block_now = 'user_block_'+id;
        func.swap(block_new, block_now);
        */
    //}

};

func.widget.outDrop = function(element) {
    $('.user_profile_stack').css({
        border:'2px dashed #ccc'
    });
};

func.widget.startDrop = function(element) {

};

func.widget.inDrop = function(drag, drop, event, ui) {

    if (drag.hasClass('user_widget_block')) return;
    var id = drag.attr('num');
    $('#user_block_'+id).fadeIn();
    $('#widget_block_'+id).fadeIn();
    $('#widget_id_'+id).fadeOut();
    drag.fadeOut();

    $("#block_sortable").sortable('refresh');
    $('.user_profile_stack').css({
        border:'2px dashed #ccc'
    });
    var widget_remain = parseInt($('#widget_total').text());
    func.widget.addCurrentWidget(1);
    func.widget.updateProgress();
    widget_remain--;
    $('#widget_total').text(widget_remain);
    // func.widget.noticeChange('success', 'Your widget has been added !');

};

func.widget.stopDrop = function(drag, drop) {

};

var widget_init = 1;
// INIT WIDGET DATA
func.widget.addNumberWigdet(widgets.length+widget_init);
func.widget.addCurrentWidget(widget_init);
func.widget.updateProgress();

// DRAG WIGET
(function (window, angular, undefined) {
    'use strict';
    var jqyoui = angular.module('ngDragDrop', []).service('ngDragDropService', ['$timeout', '$parse', function ($timeout, $parse) {
            this.callEventCallback = function (scope, callbackName, event, ui) {
                if (!callbackName) return;

                var objExtract = extract(callbackName),
                    callback = objExtract.callback,
                    constructor = objExtract.constructor,
                    args = [event, ui].concat(objExtract.args);

                // call either $scoped method i.e. $scope.dropCallback or constructor's method i.e. this.dropCallback
                scope.$apply((scope[callback] || scope[constructor][callback]).apply(scope, args));

                function extract(callbackName) {
                    var atStartBracket = callbackName.indexOf('(') !== -1 ? callbackName.indexOf('(') : callbackName.length,
                        atEndBracket = callbackName.lastIndexOf(')') !== -1 ? callbackName.lastIndexOf(')') : callbackName.length,
                        args = callbackName.substring(atStartBracket + 1, atEndBracket), // matching function arguments inside brackets
                        constructor = callbackName.match(/^[^.]+.\s*/)[0].slice(0, -1); // matching a string upto a dot to check ctrl as syntax
                    constructor = scope[constructor] && typeof scope[constructor].constructor === 'function' ? constructor : null;

                    return {
                        callback: callbackName.substring(constructor && constructor.length + 1 || 0, atStartBracket),
                        args: (args && args.split(',') || []).map(function (item) {
                            return $parse(item)(scope);
                        }),
                        constructor: constructor
                    }
                }
            };

            this.invokeDrop = function ($draggable, $droppable, event, ui) {
                func.widget.inDrop($draggable, $droppable, event, ui);

                var dragModel = '',
                    dropModel = '',
                    dragSettings = {},
                    dropSettings = {},
                    jqyoui_pos = null,
                    dragItem = {},
                    dropItem = {},
                    dragModelValue,
                    dropModelValue,
                    $droppableDraggable = null,
                    droppableScope = $droppable.scope(),
                    draggableScope = $draggable.scope();

                dragModel = $draggable.ngattr('ng-model');
                dropModel = $droppable.ngattr('ng-model');
                dragModelValue = draggableScope.$eval(dragModel);
                dropModelValue = droppableScope.$eval(dropModel);

                $droppableDraggable = $droppable.find('[jqyoui-draggable]:last,[data-jqyoui-draggable]:last');
                dropSettings = droppableScope.$eval($droppable.attr('jqyoui-droppable') || $droppable.attr('data-jqyoui-droppable')) || [];
                dragSettings = draggableScope.$eval($draggable.attr('jqyoui-draggable') || $draggable.attr('data-jqyoui-draggable')) || [];

                // Helps pick up the right item
                dragSettings.index = this.fixIndex(draggableScope, dragSettings, dragModelValue);
                dropSettings.index = this.fixIndex(droppableScope, dropSettings, dropModelValue);

                jqyoui_pos = angular.isArray(dragModelValue) ? dragSettings.index : null;
                dragItem = angular.copy(angular.isArray(dragModelValue) ? dragModelValue[jqyoui_pos] : dragModelValue);

                if (angular.isArray(dropModelValue) && dropSettings && dropSettings.index !== undefined) {
                    dropItem = dropModelValue[dropSettings.index];
                } else if (!angular.isArray(dropModelValue)) {
                    dropItem = dropModelValue;
                } else {
                    dropItem = {};
                }
                dropItem = angular.copy(dropItem);

                if (dragSettings.animate === true) {
                    this.move($draggable, $droppableDraggable.length > 0 ? $droppableDraggable : $droppable, null, 'fast', dropSettings, null);
                    this.move($droppableDraggable.length > 0 && !dropSettings.multiple ? $droppableDraggable : [], $draggable.parent('[jqyoui-droppable],[data-jqyoui-droppable]'), jqyoui.startXY, 'fast', dropSettings, angular.bind(this, function() {
                        $timeout(angular.bind(this, function() {
                            // Do not move this into move() to avoid flickering issue
                            $draggable.css({'position': 'relative', 'left': '', 'top': ''});
                            // Angular v1.2 uses ng-hide to hide an element not display property
                            // so we've to manually remove display:none set in this.move()
                            $droppableDraggable.css({'position': 'relative', 'left': '', 'top': '', 'display': ''});

                            //this.mutateDraggable(draggableScope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable);
                           // this.mutateDroppable(droppableScope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos);
                            this.callEventCallback(droppableScope, dropSettings.onDrop, event, ui);
                        }));
                    }));
                } else {
                    $timeout(angular.bind(this, function() {
                        //this.mutateDraggable(draggableScope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable);
                        //this.mutateDroppable(droppableScope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos);
                        this.callEventCallback(droppableScope, dropSettings.onDrop, event, ui);
                    }));
                }
            };

            this.move = function ($fromEl, $toEl, toPos, duration, dropSettings, callback) {
                if ($fromEl.length === 0) {
                    if (callback) {
                        window.setTimeout(function () {
                            callback();
                        }, 300);
                    }
                    return false;
                }

                var zIndex = 9999,
                    fromPos = $fromEl[dropSettings.containment || 'offset'](),
                    wasVisible = $toEl && $toEl.is(':visible'),
                    hadNgHideCls = $toEl.hasClass('ng-hide');

                if (toPos === null && $toEl.length > 0) {
                    if (($toEl.attr('jqyoui-draggable') || $toEl.attr('data-jqyoui-draggable')) !== undefined && $toEl.ngattr('ng-model') !== undefined && $toEl.is(':visible') && dropSettings && dropSettings.multiple) {
                        toPos = $toEl[dropSettings.containment || 'offset']();
                        if (dropSettings.stack === false) {
                            toPos.left += $toEl.outerWidth(true);
                        } else {
                            toPos.top += $toEl.outerHeight(true);
                        }
                    } else {
                        // Angular v1.2 uses ng-hide to hide an element
                        // so we've to remove it in order to grab its position
                        if (hadNgHideCls) $toEl.removeClass('ng-hide');
                        toPos = $toEl.css({'visibility': 'hidden', 'display': 'block'})[dropSettings.containment || 'offset']();
                        $toEl.css({'visibility': '', 'display': wasVisible ? 'block' : 'none'});
                    }
                }

                $fromEl.css({'position': 'absolute', 'z-index': zIndex})
                    .css(fromPos)
                    .animate(toPos, duration, function () {
                        // Angular v1.2 uses ng-hide to hide an element
                        // and as we remove it above, we've to put it back to
                        // hide the element (while swapping) if it was hidden already
                        // because we remove the display:none in this.invokeDrop()
                        if (hadNgHideCls) $toEl.addClass('ng-hide');
                        if (callback) callback();
                    });
            };

            this.mutateDroppable = function (scope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos) {
                var dropModelValue = scope.$eval(dropModel);
                scope.dndDragItem = dragItem;

                if (angular.isArray(dropModelValue)) {
                    if (dropSettings && dropSettings.index >= 0) {
                        dropModelValue[dropSettings.index] = dragItem;
                    } else {
                        dropModelValue.push(dragItem);
                    }
                    if (dragSettings && dragSettings.placeholder === true) {
                        dropModelValue[dropModelValue.length - 1]['jqyoui_pos'] = jqyoui_pos;
                    }
                } else {
                    $parse(dropModel + ' = dndDragItem')(scope);
                    if (dragSettings && dragSettings.placeholder === true) {
                        dropModelValue['jqyoui_pos'] = jqyoui_pos;
                    }
                }
            };

            this.mutateDraggable = function (scope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable) {
                var isEmpty = angular.equals(dropItem, {}),
                    dragModelValue = scope.$eval(dragModel);
                console.log('MUTE DRAGABLE');
                scope.dndDropItem = dropItem;

                if (dragSettings && dragSettings.placeholder) {
                    if (dragSettings.placeholder != 'keep') {
                        if (angular.isArray(dragModelValue) && dragSettings.index !== undefined) {
                            dragModelValue[dragSettings.index] = dropItem;
                        } else {
                            $parse(dragModel + ' = dndDropItem')(scope);
                        }
                    }
                } else {
                    if (angular.isArray(dragModelValue)) {
                        if (isEmpty) {
                            if (dragSettings && ( dragSettings.placeholder !== true && dragSettings.placeholder !== 'keep' )) {
                                dragModelValue.splice(dragSettings.index, 1);
                            }
                        } else {
                            dragModelValue[dragSettings.index] = dropItem;
                        }
                    } else {
                        // Fix: LIST(object) to LIST(array) - model does not get updated using just scope[dragModel] = {...}
                        // P.S.: Could not figure out why it happened
                        $parse(dragModel + ' = dndDropItem')(scope);
                        if (scope.$parent) {
                            $parse(dragModel + ' = dndDropItem')(scope.$parent);
                        }
                    }
                }

                $draggable.css({'z-index': '', 'left': '', 'top': ''});
            };

            this.fixIndex = function (scope, settings, modelValue) {
                if (settings.applyFilter && angular.isArray(modelValue) && modelValue.length > 0) {
                    var dragModelValueFiltered = scope[settings.applyFilter](),
                        lookup = dragModelValueFiltered[settings.index],
                        actualIndex = undefined;

                    modelValue.forEach(function (item, i) {
                        if (angular.equals(item, lookup)) {
                            actualIndex = i;
                        }
                    });

                    return actualIndex;
                }

                return settings.index;
            };
        }]).directive('jqyouiDraggable', ['ngDragDropService', function (ngDragDropService) {
            return {
                require: '?jqyouiDroppable',
                restrict: 'A',
                link: function (scope, element, attrs) {

                    var dragSettings, jqyouiOptions, zIndex;
                    var updateDraggable = function (newValue, oldValue) {
                        if (newValue) {
                            dragSettings = scope.$eval(element.attr('jqyoui-draggable') || element.attr('data-jqyoui-draggable')) || {};
                            jqyouiOptions = scope.$eval(attrs.jqyouiOptions) || {};
                            element
                                .draggable({disabled: false})
                                .draggable(jqyouiOptions)
                                .draggable({

                                    start: function (event, ui) {
                                        func.widget.startDrag(this);
                                        zIndex = angular.element(jqyouiOptions.helper ? ui.helper : this).css('z-index');
                                        angular.element(jqyouiOptions.helper ? ui.helper : this).css('z-index', 9999);
                                        jqyoui.startXY = angular.element(this)[dragSettings.containment || 'offset']();
                                        ngDragDropService.callEventCallback(scope, dragSettings.onStart, event, ui);
                                    },
                                    stop: function (event, ui) {
                                        func.widget.stopDrag(this);
                                        angular.element(jqyouiOptions.helper ? ui.helper : this).css('z-index', zIndex);
                                        ngDragDropService.callEventCallback(scope, dragSettings.onStop, event, ui);
                                    },
                                    drag: function (event, ui) {
                                        // CUSTOM IN DRAG
                                        func.widget.inDrag(this);
                                        ngDragDropService.callEventCallback(scope, dragSettings.onDrag, event, ui);
                                    }
                                });
                        } else {
                            element.draggable({disabled: true});
                        }
                    };
                    scope.$watch(function () {
                        return scope.$eval(attrs.drag);
                    }, updateDraggable);
                    updateDraggable();

                    element.on('$destroy', function () {
                        element.draggable('destroy');
                    });
                }
            };
        }]).directive('jqyouiDroppable', ['ngDragDropService', function (ngDragDropService) {
            return {
                restrict: 'A',
                priority: 1,
                link: function (scope, element, attrs) {
                    var dropSettings;
                    var updateDroppable = function (newValue, oldValue) {
                        if (newValue) {
                            dropSettings = scope.$eval(angular.element(element).attr('jqyoui-droppable') || angular.element(element).attr('data-jqyoui-droppable')) || {};
                            element
                                .droppable({disabled: false})
                                .droppable(scope.$eval(attrs.jqyouiOptions) || {})
                                .droppable({
                                    over: function (event, ui) {
                                        // CUSTOM
                                        func.widget.overDrop(this);
                                        ngDragDropService.callEventCallback(scope, dropSettings.onOver, event, ui);
                                    },
                                    out: function (event, ui) {
                                        func.widget.outDrop(this);
                                        ngDragDropService.callEventCallback(scope, dropSettings.onOut, event, ui);
                                    },
                                    drop: function (event, ui) {
                                        if (angular.element(ui.draggable).ngattr('ng-model') && attrs.ngModel) {
                                            ngDragDropService.invokeDrop(angular.element(ui.draggable), angular.element(this), event, ui);
                                        } else {
                                            ngDragDropService.callEventCallback(scope, dropSettings.onDrop, event, ui);
                                        }
                                    }
                                });
                        } else {
                            element.droppable({disabled: true});
                        }
                    };

                    scope.$watch(function () {
                        return scope.$eval(attrs.drop);
                    }, updateDroppable);
                    updateDroppable();

                    element.on('$destroy', function () {
                        element.droppable('destroy');
                    });
                }
            };
        }]);

    $.fn.ngattr = function (name, value) {
        var element = angular.element(this).get(0);
        return element.getAttribute(name) || element.getAttribute('data-' + name);
    };
})(window, window.angular);    